<!DOCTYPE html>
<html lang="en">

<head>

    <meta charset="UTF-8">

    <link rel="apple-touch-icon" type="image/png"
        href="https://cpwebassets.codepen.io/assets/favicon/apple-touch-icon-5ae1a0698dcc2402e9712f7d01ed509a57814f994c660df9f7a952f3060705ee.png" />
    <meta name="apple-mobile-web-app-title" content="CodePen">

    <link rel="shortcut icon" type="image/x-icon"
        href="https://cpwebassets.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico" />

    <link rel="mask-icon" type=""
        href="https://cpwebassets.codepen.io/assets/favicon/logo-pin-8f3771b1072e3c38bd662872f6b673a722f4b3ca2421637d5596661b4e2132cc.svg"
        color="#111" />


    <title>CodePen - Canvas: 封装Canvas绘图API &amp;&amp; 绘制时钟</title>

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">



    <style>
        body {
            width: 100vw;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: flex-start;
        }
    </style>

    <script>
        window.console = window.console || function (t) { };
    </script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>


    <script>
        if (document.location.search.match(/type=embed/gi)) {
            window.parent.postMessage("resize", "*");
        }
    </script>


</head>

<body translate="no">
    <canvas id="canvasOne" width="600" height="300"></canvas>
    <script
        src="https://cpwebassets.codepen.io/assets/common/stopExecutionOnTimeout-157cd5b220a5c80d4ff8e0e70ac069bffd87a61252088146915e8726e5d9f147.js"></script>


    <script id="rendered-js">
        window.addEventListener('load', eventWindowLoaded, false);

        var Debugger = function () { };

        Debugger.log = function (message) {
            try {
                console.log(message);
            } catch (exception) {
                return;
            }
        };

        function eventWindowLoaded() {
            canvasApp();
        }

        function canvasApp() {

            Debugger.log('Drawing Canvas');

            // 声明Canvas对象
            var canvas;
            // 声明context对象
            function initDrawCanvas(canvas, ctx) {
                this.canvas = canvas;
                this.ctx = ctx;
            }

            // 封装矩形
            // 矩形包括： 填充矩形、边框矩形和清除矩形区域
            // @param {Number} x - 矩形起点的x坐标
            // @param {Number} y - 矩形起点的y坐标
            // @param {Number} width - 矩形宽度
            // @param {Number} height - 矩形高度
            // @param {Boolean} isClear - 是否绘制清除画布的矩形区域； true则是绘制一个清除画布矩形区域, false就是绘制其他两种矩形
            // @param {Boolean} isFill - 是否填充；true绘制填充矩形， false绘制边框矩形
            // @param {String} color - 矩形颜色
            function drawRect(x, y, width, height, isClear, isFill, color) {
                // 为true表示绘制清除画布的矩形区域，那么传入的isFill，color值可以为任意值
                if (isClear) {
                    ctx.clearRect(x, y, width, height);
                } else {
                    if (isFill) {
                        ctx.fillStyle = color;
                        ctx.fillRect(x, y, width, height);
                    } else {
                        ctx.strokeStyle = color;
                        ctx.strokeRect(x, y, width, height);
                    }
                }
            }

            // 绘制圆弧
            // 分类：填充圆弧和边框圆弧
            // @param {Number} x - 圆心x轴坐标
            // @param {Number} y - 圆心y轴坐标
            // @param {Number} radius - 圆弧的半径
            // @param {Number} startAngle - 开始的弧度（开始角度），只接受弧度单位，需要将deg先转换为rad： rad = Math.PI * deg / 180
            // @param {Number} endAngle - 结束的弧度（结束的角度）
            // @param {Boolean} anticlockwise - 旋转方向；true为逆时针，false为顺时针
            // @param {Boolean} isFill - 是否填充；true为填充，false为边框
            // @param {Boolean} isOnlyArc - 是否仅绘制弧边，如果使用closePath()终点和起点会连接到一起，否则不会。true时不连接，false连接
            // @param {String} color - 圆弧的颜色
            function drawArc(x, y, radius, startAngle, endAngle, anticlockwise, isOnlyArc, isFill, color) {
                if (isFill) {
                    ctx.fillStyle = color;
                    ctx.beginPath();
                    ctx.arc(x, y, radius, getAngle(startAngle), getAngle(endAngle), anticlockwise);
                    ctx.closePath();
                    ctx.fill();
                } else {
                    ctx.strokeStyle = color;
                    ctx.beginPath();
                    ctx.arc(x, y, radius, getAngle(startAngle), getAngle(endAngle), anticlockwise);
                    if (isOnlyArc) {

                    } else {
                        ctx.closePath();
                    }
                    ctx.stroke();
                }
            }

            // 将角度转换为弧度
            // @param {Number} deg - 角度值
            function getAngle(deg) {
                return Math.PI * deg / 180;
            }

            // 绘制扇形
            // @param {Number} x - 圆心x轴坐标
            // @param {Number} y - 圆心y轴坐标
            // @param {Number} radius - 圆半径
            // @param {Number} startAngle - 开始弧度
            // @param {Number} endAngle - 结束弧度
            // @param {Number} anticlockwise - 旋转方向， true逆时针，false顺时针
            // @param {Boolean} isFill - true为填充，false为边框
            // @param {String} color - 扇形的颜色
            function drawSector(x, y, radius, startAngle, endAngle, anticlockwise, isFill, color) {
                ctx.beginPath();
                ctx.moveTo(x, y);
                ctx.arc(x, y, radius, getAngle(startAngle), getAngle(endAngle), false);
                ctx.closePath();

                if (isFill) {
                    ctx.fillStyle = color;
                    ctx.fill();
                } else {
                    ctx.strokeStyle = color;
                    ctx.stroke();
                }
            }

            // 绘制圆角矩形
            // @param {Number} x - 矩形左上角x轴坐标
            // @param {Number} y - 矩形左上角y轴坐标
            // @param {Number} width - 矩形的宽度
            // @param {Number} height - 矩形的高度
            // @param {Number} radius - 矩形圆角的半径
            // @param {Boolean} isFill - 是否绘制填充，true填充，false边框
            // @param {String} color - 矩形的颜色
            function drawRoundedRect(x, y, width, height, radius, isFill, color) {
                ctx.beginPath();
                ctx.moveTo(x + radius, y);
                ctx.arcTo(x + width, y, x + width, y + radius, radius);
                ctx.arcTo(x + width, y + height, x + width - radius, y + height, radius);
                ctx.arcTo(x, y + height, x, y + height - radius, radius);
                ctx.arcTo(x, y, x + radius, y, radius);
                ctx.closePath();

                if (isFill) {
                    ctx.fillStyle = color;
                    ctx.fill();
                } else {
                    ctx.strokeStyle = color;
                    ctx.stroke();
                }
            }

            // 绘制实线线段
            // @param {Number} startX - 线段起点x轴坐标
            // @param {Number} startY - 线段起点y轴坐标
            // @param {Number} endX - 线段终点x轴坐标
            // @param {Number} endY - 线段终点y轴坐标
            // @param {Number} lineWidth - 线宽
            // @param {String} color - 线颜色
            function drawSolidLine(startX, startY, endX, endY, lineWidth, color) {
                ctx.save();
                ctx.strokeStyle = color;
                ctx.lineWidth = lineWidth;
                ctx.beginPath();
                ctx.moveTo(startX, startY);
                ctx.lineTo(endX, endY);
                ctx.stroke();
                ctx.restore();
            }

            // 绘制点划线
            // @param {Number} startX - 线段起点x轴坐标
            // @param {Number} startY - 线段起点y轴坐标
            // @param {Number} endX - 线段终点x轴坐标
            // @param {Number} endY - 线段终点y轴坐标
            // @param {Array} setLineDash - 点划线间距
            // @param {Number} lineWidth - 线宽
            // @param {String} color - 线段颜色
            function drawDashLine(startX, startY, endX, endY, setLineDash, lineWidth, color) {
                ctx.save();
                ctx.lineWidth = lineWidth;
                ctx.strokeStyle = color;
                ctx.beginPath();
                ctx.setLineDash(setLineDash);
                ctx.moveTo(startX, startY);
                ctx.lineTo(endX, endY);
                ctx.closePath();
                ctx.stroke();
                ctx.restore();
            }

            // 绘制圆点线 虚线
            // @param {Number} startX - 线段起点x轴坐标
            // @param {Number} startY - 线段起点y轴坐标
            // @param {Number} endX - 线段终点x轴坐标
            // @param {Number} endY - 线段终点y轴坐标
            // @param {Number} interval - 间隔
            // @param {Number} radius - 圆点半径
            // @param {String} color - 线段颜色
            function drawDottedLine(startX, startY, endX, endY, radius, interval, color) {
                if (!interval) {
                    interval = 5;
                }
                var isHorizontal = true;
                if (startX == endX) {
                    isHorizontal = false;
                }

                var len = isHorizontal ? endX - startX : endY - startY;

                ctx.beginPath();
                ctx.strokeStyle = color;
                ctx.fillStyle = color;
                ctx.moveTo(startX, startY);
                var progress = 0;
                while (len > progress) {
                    progress += interval;

                    if (progress > len) {
                        progress = len;
                    }

                    if (isHorizontal) {
                        ctx.moveTo(startX + progress, startY);
                        ctx.arc(startX + progress, startY, radius, 0, Math.PI * 2, true);
                        ctx.fill();
                    } else {
                        ctx.moveTo(startX, endX + progress);
                        ctx.arc(startX, startY + progress, radius, 0, Math.PI * 2, true);
                        ctx.fill();
                    }
                }
            }

            // From: http://www.dbp-consulting.com/tutorials/canvas/CanvasArrow.html
            // Draw arrow head
            function drawHead(x0, y0, x1, y1, x2, y2, style, color, width) {
                if (typeof x0 == 'string') {
                    x0 = parseInt(x0);
                }
                if (typeof y0 == 'string') {
                    y0 = parseInt(y0);
                }
                if (typeof x1 == 'string') {
                    x1 = parseInt(x1);
                }
                if (typeof y1 == 'string') {
                    y1 = parseInt(y1);
                }
                if (typeof x2 == 'string') {
                    x2 = parseInt(x2);
                }
                if (typeof y2 == 'string') {
                    y2 = parseInt(y2);
                }

                var radius = 3,
                    twoPI = 2 * Math.PI;

                ctx.save();
                ctx.beginPath();
                ctx.strokeStyle = color;
                ctx.fillStyle = color;
                ctx.lineWidth = width;
                ctx.moveTo(x0, y0);
                ctx.lineTo(x1, y1);
                ctx.lineTo(x2, y2);

                switch (style) {
                    case 0:
                        var backdist = Math.sqrt((x2 - x0) * (x2 - x0) + (y2 - y0) * (y2 - y0));
                        ctx.arcTo(x1, y1, x0, y0, .55 * backdist);
                        ctx.fill();
                        break;
                    case 1:
                        ctx.beginPath();
                        ctx.moveTo(x0, y0);
                        ctx.lineTo(x1, y1);
                        ctx.lineTo(x2, y2);
                        ctx.lineTo(x0, y0);
                        ctx.fill();
                        break;
                    case 2:
                        ctx.stroke();
                        break;
                    case 3:
                        var cpx = (x0 + x1 + x2) / 3;
                        var cpy = (y0 + y1 + y2) / 3;
                        ctx.quadraticCurveTo(cpx, cpy, x0, y0);
                        ctx.fill();
                        break;
                    case 4:
                        var cp1x, cp1y, cp2x, cp2y, backdist;
                        var shiftamt = 5;
                        if (x2 == x0) {
                            backdist = y2 - y0;
                            cp1x = (x1 + x0) / 2;
                            cp2x = (x1 + x0) / 2;
                            cp1y = y1 + backdist / shiftamt;
                            cp2y = y1 - backdist / shiftamt;
                        } else {
                            backdist = Math.sqrt((x2 - x0) * (x2 - x0) + (y2 - y0) * (y2 - y0));
                            var xback = (x0 + x2) / 2;
                            var yback = (y0 + y2) / 2;
                            var xmid = (xback + x1) / 2;
                            var ymid = (yback + y1) / 2;
                            var m = (y2 - y0) / (x2 - x0);
                            var dx = backdist / (2 * Math.sqrt(m * m + 1)) / shiftamt;
                            var dy = m * dx;
                            cp1x = xmid - dx;
                            cp1y = ymid - dy;
                            cp2x = xmid + dx;
                            cp2y = ymid + dy;
                        }
                        ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x0, y0);
                        ctx.fill();
                        break;
                }

                ctx.restore();
            }

            // draw arrow
            function drawArrow(x1, y1, x2, y2, style, which, angle, d, color, width) {
                if (typeof x1 == 'string') {
                    x1 = parseInt(x1);
                }
                if (typeof y1 == 'string') {
                    y1 = parseInt(y1);
                }
                if (typeof x2 == 'string') {
                    x2 = parseInt(x2);
                }
                if (typeof y2 == 'string') {
                    y2 = parseInt(y2);
                }
                style = typeof style != 'undefined' ? style : 3;
                which = typeof which != 'undefined' ? which : 1;
                angle = typeof angle != 'undefined' ? angle : Math.PI / 9;
                d = typeof d != 'undefined' ? d : 10;
                color = typeof color != 'undefined' ? color : '#000';
                width = typeof width != 'undefined' ? width : 1;
                var toDrawHead = typeof style != 'function' ? drawHead : style;
                var dist = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
                var ratio = (dist - d / 3) / dist;
                var tox, toy, fromx, fromy;
                if (which & 1) {
                    tox = Math.round(x1 + (x2 - x1) * ratio);
                    toy = Math.round(y1 + (y2 - y1) * ratio);
                } else {
                    tox = x2;
                    toy = y2;
                }

                if (which & 2) {
                    fromx = x1 + (x2 - x1) * (1 - ratio);
                    fromy = y1 + (y2 - y1) * (1 - ratio);
                } else {
                    fromx = x1;
                    fromy = y1;
                }

                ctx.beginPath();
                ctx.strokeStyle = color;
                ctx.lineWidth = width;
                ctx.moveTo(fromx, fromy);
                ctx.lineTo(tox, toy);
                ctx.stroke();

                var lineangle = Math.atan2(y2 - y1, x2 - x1);
                var h = Math.abs(d / Math.cos(angle));
                if (which & 1) {
                    var angle1 = lineangle + Math.PI + angle;
                    var topx = x2 + Math.cos(angle1) * h;
                    var topy = y2 + Math.sin(angle1) * h;
                    var angle2 = lineangle + Math.PI - angle;
                    var botx = x2 + Math.cos(angle2) * h;
                    var boty = y2 + Math.sin(angle2) * h;
                    toDrawHead(topx, topy, x2, y2, botx, boty, style, color, width);
                }

                if (which & 2) {
                    var angle1 = lineangle + angle;
                    var topx = x1 + Math.cos(angle1) * h;
                    var topy = y1 + Math.sin(angle1) * h;
                    var angle2 = lineangle - angle;
                    var botx = x1 + Math.cos(angle2) * h;
                    var boty = y1 + Math.sin(angle2) * h;
                    toDrawHead(topx, topy, x1, y1, botx, boty, style, color, width);
                }
            }

            // draw arced arrow
            function drawArcedArrow(x, y, r, startangle, endangle, anticlockwise, style, which, angle, d, color, width) {
                style = typeof style != 'undefined' ? style : 3;
                which = typeof which != 'undefined' ? which : 1;
                angle = typeof angle != 'undefined' ? angle : Math.PI / 8;
                d = typeof d != 'undefined' ? d : 10;
                color = typeof color != 'undefined' ? color : '#000';
                width = typeof width != 'undefined' ? width : 1;

                ctx.save();
                ctx.beginPath();
                ctx.lineWidth = width;
                ctx.strokeStyle = color;
                ctx.arc(x, y, r, startangle, endangle, anticlockwise);
                ctx.stroke();
                var sx, sy, lineangle, destx, desty;
                ctx.strokeStyle = 'rgba(0,0,0,0)';
                if (which & 1) {
                    sx = Math.cos(startangle) * r + x;
                    sy = Math.sin(startangle) * r + y;
                    lineangle = Math.atan2(x - sx, sy - y);
                    if (anticlockwise) {
                        destx = sx + 10 * Math.cos(lineangle);
                        desty = sy + 10 * Math.sin(lineangle);
                    } else {
                        destx = sx - 10 * Math.cos(lineangle);
                        desty = sy - 10 * Math.sin(lineangle);
                    }
                    drawArrow(sx, sy, destx, desty, style, 2, angle, d, color, width);
                }

                if (which & 2) {
                    sx = Math.cos(endangle) * r + x;
                    sy = Math.sin(endangle) * r + y;
                    lineangle = Math.atan2(x - sx, sy - y);
                    if (anticlockwise) {
                        destx = sx - 10 * Math.cos(lineangle);
                        desty = sy - 10 * Math.sin(lineangle);
                    } else {
                        destx = sx + 10 * Math.cos(lineangle);
                        desty = sy + 10 * Math.sin(lineangle);
                    }
                    drawArrow(sx, sy, destx, desty, style, 2, angle, d, color, width);
                }
                ctx.restore();
            }

            // 绘制正多边形
            // @param {Number} xCenter 中心坐标X点
            // @param {Number} yCenter 中心坐标Y点
            // @param {Number} radius 外圆半径
            // @param {Number} sides 多边形边数
            // @param {Number} sideIndent (0 ~ 1)
            // @param {Number} alpha 角度 默认270度
            // @param {Boolean} isFill true填充，false边框
            // @param {String} color 正多边形颜色 
            function drawStarPolygons(xCenter, yCenter, radius, sides, sideIndent, alpha, isFill, color) {

                var sideIndentRadius = radius * (sideIndent || 0.38);
                var radAngle = alpha ? alpha * Math.PI / 180 : -Math.PI / 2;
                var radAlpha = Math.PI * 2 / sides / 2;

                ctx.save();
                ctx.beginPath();

                var xPos = xCenter + Math.cos(radAngle) * radius;
                var yPos = yCenter + Math.sin(radAngle) * radius;

                ctx.moveTo(xPos, yPos);

                for (var i = 1; i <= sides * 2; i++) {
                    var rad = radAlpha * i + radAngle;
                    var len = i % 2 ? sideIndentRadius : radius;
                    var xPos = xCenter + Math.cos(rad) * len;
                    var yPos = yCenter + Math.sin(rad) * len;

                    ctx.lineTo(xPos, yPos);

                }

                ctx.closePath();

                if (isFill) {
                    ctx.fillStyle = color;
                    ctx.fill();
                } else {
                    ctx.strokeStyle = color;
                    ctx.stroke();
                }
            }


            var canvas = document.getElementById('canvasOne');
            var ctx = canvas.getContext('2d');
            var w = canvas.width = window.innerWidth;
            var h = canvas.height = window.innerHeight;
            initDrawCanvas(canvas, ctx);

            // 绘制一个时钟


            var radius = 150;
            var handTruncation = canvas.width / 25;
            var hourHandTruncation = canvas.width / 10;

            // 绘制时钟刻度盘
            function drawClockFace() {
                // step1: 绘制时钟的外圆和圆心
                ctx.lineWidth = 4;
                ctx.translate(w / 2, h / 2);

                drawArc(0, 0, radius, 0, 360, true, true, false, '#000');
                drawArc(0, 0, 10, 0, 360, true, true, true, '#000');


                // step2: 绘制时钟刻度线
                for (var i = 0; i < 60; i++) {
                    var rad = getAngle(i * 6);
                    ctx.save();
                    ctx.rotate(rad);
                    if (i % 5 === 0) {
                        drawSolidLine(radius - 15, -1, radius - 4, -1, 4, '#000');

                    } else {
                        drawSolidLine(radius - 8, -1, radius - 4, -1, 2, '#999');
                    }
                    ctx.restore();
                }

                // step3: 绘制时钟数字
                ctx.font = radius * 0.15 + "px arial";
                ctx.textBaseline = "middle";
                ctx.textAlign = "center";
                for (var i = 1; i < 13; i++) {
                    var ang = getAngle(30 * i);
                    ctx.fillText(i.toString(), radius * 0.80 * Math.sin(ang), -radius * 0.80 * Math.cos(ang));
                }

            }

            // 绘制时钟针
            function drawHand(angle, length, width, color) {
                var endX = Math.sin(angle) * length;
                var endY = -Math.cos(angle) * length;
                ctx.lineCap = 'round';
                drawSolidLine(0, 0, endX, endY, width, color);
            }

            function drawHands(radius) {
                var now = new Date();
                var hour = now.getHours();
                var minute = now.getMinutes();
                var second = now.getSeconds();

                hour = hour % 12;
                hour = getAngle(30) * hour + getAngle(30) * minute / 60 + getAngle(30) * second / 3600;
                minute = Math.PI / 30 * minute + second * Math.PI / 1800;
                second = Math.PI / 30 * second;
                drawHand(hour, radius * 0.4, radius * 0.07); // 时针
                drawHand(minute, radius * 0.6, radius * 0.05); // 分针
                drawHand(second, radius * 0.7, radius * 0.03, 'red'); // 秒针
            }

            function drawClock() {
                ctx.resetTransform();
                drawRect(0, 0, w, h, true);
                drawClockFace();
                drawHands(radius);
            }
            setInterval(drawClock, 1000);

        }
//# sourceURL=pen.js
    </script>
</body>

</html>